package com.caocaog.market.pay.wxpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.caocaog.market.entity.OrderBase;
import com.caocaog.market.entity.OrderRefund;
import com.caocaog.market.finance.entity.MkBillBase;
import com.caocaog.market.finance.service.MkBillBaseService;
import com.caocaog.market.pay.vo.PayOrderVo;
import com.caocaog.market.pay.vo.RefundOrderVo;
import com.caocaog.market.service.IOrderBaseService;
import com.caocaog.market.service.IOrderRefundService;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.bdPay.BDPayUtil;
import org.jeecg.common.bdPay.ByteDanceUrlConstants;
import org.jeecg.common.bdPay.RestTemplateUtil;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
@Component
public class BDPay {

    private IOrderRefundService orderRefundService;
    private IOrderBaseService orderBaseService;
    private MkBillBaseService mkBillBaseService;
    private BDPayUtil bdPayUtil;
    private RestTemplateUtil restTemplateUtil;

    @Value("${tiktok.app-id}")
    private String appId;


    private static final String ERROR_INFO = "error_info";

    public JSONObject ttAppletPay(PayOrderVo orderVO) {
        JSONObject returnJson = new JSONObject();
        try {
            //加签验签的参数需要排序
            Map<String, Object> params = new TreeMap<>();
            //小程序APPID
            params.put("app_id", appId);
            //开发者侧的订单号。需保证同一小程序下不可重复
            params.put("out_order_no", orderVO.getOutTradeNo());
            //支付价格。单位为[分]，取值范围：[1,10000000000]  100元 = 100*100 分
            params.put("total_amount", (orderVO.getTotalPrice().multiply(new BigDecimal(100))).stripTrailingZeros().toPlainString());
            //商品描述。
            params.put("subject", orderVO.getBody());
            //商品详情
            params.put("body", orderVO.getAttach());
            //订单过期时间(秒) 5min-2day
            params.put("valid_time", 1800);
            //类型
            params.put("cp_extra", "0");
            //签名，详见https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/ecpay/TE
            String sign = bdPayUtil.getSign(params);
//            params.put("sign", sign);

            //以JSON格式拼好以下参数发送请求
            JSONObject payJson = new JSONObject();
            payJson.put("app_id", appId);
            payJson.put("out_order_no", orderVO.getOutTradeNo());
            //此处需要传入一个数值类型，string会报错。。
            payJson.put("total_amount", new BigDecimal((orderVO.getTotalPrice().multiply(new BigDecimal(100))).stripTrailingZeros().toPlainString()));
            payJson.put("subject", orderVO.getBody());
            payJson.put("body", orderVO.getAttach());
            payJson.put("valid_time", 1800);
            payJson.put("cp_extra", "0");
            payJson.put("sign", sign);

            log.info(payJson.toString());
            //预下单接口
            String result = restTemplateUtil.byteDancePostRequest(payJson, ByteDanceUrlConstants.CREATE_ORDER);
            if (!"".equals(result)) {

                JSONObject jsonObject = JSON.parseObject(result);
                log.info(jsonObject.toString());
                String errNo = jsonObject.getString("err_no");
                if ("0".equals(errNo)) {
                    JSONObject data = jsonObject.getJSONObject("data");
                    String orderId = data.getString("order_id");
                    String orderToken = data.getString("order_token");
                    if (null != orderId && null != orderToken) {
                        //前端使用此处返回的data来调起付款收银台
                        returnJson.put("order_info_json", data);
                    } else {
                        returnJson.put(ERROR_INFO, "支付参数为空");
                    }
                } else {
                    returnJson.put(ERROR_INFO, "参数错误[" + errNo + "]");
                }
            } else {
                returnJson.put(ERROR_INFO, "支付订单创建失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("抖音小程序微信支付异常：{}", e.getMessage());
            returnJson.put(ERROR_INFO, "抖音小程序微信支付异常");
        }
        log.info(returnJson.toString());
        return returnJson;
    }


    /**
     * 验证回调结果
     *
     * @param object 回调参数
     * @see <a href="https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/ecpay/APIlist/pay/#%E5%9B%9E%E8%B0%83%E5%8F%82%E6%95%B0">回调参数</a>
     */
    public JSONObject ttAppletNotify(JSONObject object) {
        boolean flag = false;
        // 随机数
        String nonce = object.getString("nonce");
        // 时间戳
        Integer timestamp = object.getInteger("timestamp");
        // 订单信息的json字符串
        String message = object.getString("msg");
        // 签名
        String msgSignature = object.getString("msg_signature");

        String signMessage = bdPayUtil.getCallbackSignature(timestamp, nonce, message);

        if (signMessage.equals(msgSignature)) {
            log.info("签名校验成功======");

            JSONObject msg = object.getJSONObject("msg");
            // 固定值SUCCESS
            String status = msg.getString("status");
            // 这里无论回调失败还是成功,都需要都各个业务层去处理相关逻辑
            flag = "SUCCESS".equals(status);
        } else {
            log.info("signMessage:" + signMessage);
            log.info("msg_signature:" + msgSignature);
            log.info("签名校验失败======");
        }

        JSONObject returnObj = new JSONObject();
        if (flag) {
            //成功处理后回复此固定值
            returnObj.put("err_no", 0);
            returnObj.put("err_tips", "success");
        } else {
            //失败回复此固定值
            returnObj.put("err_no", 400);
            returnObj.put("err_tips", "business fail");
        }

        return returnObj;
    }

    /**
     * 支付结果查询
     *
     * @param orderNo 订单号
     */
    public JSONObject ttPayResult(String orderNo) {
        JSONObject returnJson = new JSONObject();
        try {
            String outOrderNo = "P" + orderNo;
            // 加签验签的参数需要排序
            Map<String, Object> params = new TreeMap<>();
            // 小程序APPID
            params.put("app_id", appId);
            // 开发者侧的订单号。需保证同一小程序下不可重复
            params.put("out_order_no", outOrderNo);
            // 签名
            String sign = bdPayUtil.getSign(params);

            // 以JSON格式拼好以下参数发送请求
            JSONObject payJson = new JSONObject();
            payJson.put("app_id", appId);
            payJson.put("out_order_no", outOrderNo);
            payJson.put("sign", sign);

            log.info(payJson.toString());
            // 查询支付结果接口
            String result = restTemplateUtil.byteDancePostRequest(payJson, ByteDanceUrlConstants.QUERY_ORDER);
            if (!"".equals(result)) {

                JSONObject jsonObject = JSON.parseObject(result);
                log.info(jsonObject.toString());
                returnJson = jsonObject;
            } else {
                returnJson.put(ERROR_INFO, "支付结果查询失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("抖音小程序支付结果查询异常：{}", e.getMessage());
            returnJson.put(ERROR_INFO, "抖音小程序支付结果查询异常");
        }
        return returnJson;
    }

    public String ttRefund(RefundOrderVo orderVo) {

        try {
            //加签验签的参数需要排序
            Map<String, Object> params = new TreeMap<>();
            //小程序APPID
            params.put("app_id", appId);
            //商户退款单号
            params.put("out_order_no", orderVo.getOutRefundNo());
            //商户分配退款号
            params.put("out_refund_no", orderVo.getOutTradeNo());
            //退款原因
            params.put("reason", "订单[" + orderVo.getOutTradeNo() + "]退款或部分退款");
            //退款金额，单位[分]
            params.put("refund_amount", (new BigDecimal(orderVo.getRefundAmount()).multiply(new BigDecimal(100))).stripTrailingZeros().toPlainString());
            //类型
            params.put("cp_extra", "1");

            //签名
            String sign = bdPayUtil.getSign(params);
            params.put("sign", sign);

            JSONObject refundJson = new JSONObject();
            refundJson.put("out_refund_no", orderVo.getOutRefundNo());
            refundJson.put("out_order_no", orderVo.getOutTradeNo());
            refundJson.put("reason", "订单[" + orderVo.getOutTradeNo() + "]退款或部分退款");
            refundJson.put("cp_extra", "1");
            refundJson.put("app_id", appId);
            refundJson.put("sign", sign);
            refundJson.put("refund_amount", new BigDecimal((new BigDecimal(orderVo.getRefundAmount()).multiply(new BigDecimal(100))).stripTrailingZeros().toPlainString()));
            OrderRefund orderRefund = orderRefundService.getOne(new QueryWrapper<OrderRefund>().eq("pay_refund_serno", orderVo.getOutRefundNo()));

            //退款
            String result = restTemplateUtil.byteDancePostRequest(refundJson, ByteDanceUrlConstants.CREATE_REFUND);
            if (!"".equals(result)) {
                String returnMessage;
                JSONObject jsonObject = JSON.parseObject(result);
                String errNo = jsonObject.getString("err_no");
                if ("0".equals(errNo)) {
                    returnMessage = "success";
                    orderRefund.setPayStatus(1);//退款成功
                    orderRefund.setEndPayInfo(result);
                    orderRefundService.updateById(orderRefund);
                    OrderBase orderBase = orderBaseService.getById(orderRefund.getOrderId());
                    orderBase.setStatus("-3");
                    orderBaseService.updateById(orderBase);
                    //更新结算状态 4已退款
                    MkBillBase mkBillBase = mkBillBaseService.findBillIsExist(orderBase.getOrderNo());
                    if (oConvertUtils.isNotEmpty(mkBillBase)){
                        mkBillBaseService.updatePayStatusAndInsertHistory(orderBase.getOrderNo(),4);
                    }
                    log.info(orderVo.toString());
                    log.info(orderRefund.toString());
                    log.info("字节退款:" + orderVo.getOrderNo() + "-"  + orderVo.getOutRefundNo() + "-" + orderVo.getRefundAmount() + "，正在退款中");
                } else {
                    String errTips = jsonObject.getString("err_tips");
                    returnMessage = errNo + ":" + errTips;
                    log.error("字节退款:" + orderVo.getOrderNo() + "-" + orderVo.getOutRefundNo() + "-" + orderVo.getRefundAmount() + "-失败了-" + returnMessage);
                    orderRefund.setPayStatus(2); // 退款失败
                    orderRefund.setEndPayInfo(result);
                    orderRefundService.updateById(orderRefund);
                }
                return returnMessage;
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("抖音小程序退款异常：{}", e.getMessage());
        }
        return "fail";
    }

    /**
     * 退款和分账公共处理方法
     */
    /*private String updateRefundSettleCommon(String result, String outTradeNo, String settleRefundNo, BigDecimal money) {
        String returnMessage;
        JSONObject jsonObject = JSON.parseObject(result);
        String errNo = jsonObject.getString("err_no");
        if ("0".equals(errNo)) {
            returnMessage = "success";
        } else {
            String errTips = jsonObject.getString("err_tips");
            returnMessage = errNo + ":" + errTips;
        }

        return returnMessage;
    }*/

    @Autowired
    public void setMkBillBaseService(MkBillBaseService mkBillBaseService) {
        this.mkBillBaseService = mkBillBaseService;
    }

    @Autowired
    public void setOrderBaseService(IOrderBaseService orderBaseService) {
        this.orderBaseService = orderBaseService;
    }

    @Autowired
    public void setOrderRefundService(IOrderRefundService orderRefundService) {
        this.orderRefundService = orderRefundService;
    }

    @Autowired
    public void setBdPayUtil(BDPayUtil bdPayUtil) {
        this.bdPayUtil = bdPayUtil;
    }

    @Autowired
    public void setRestTemplateUtil(RestTemplateUtil restTemplateUtil) {
        this.restTemplateUtil = restTemplateUtil;
    }
}
